/**
 * Copyright (c) 2022 Wei-Lun Hsu. All Rights Reserved.
 */
/** @file zb32l03x_hal_spi.c
 *
 * @author Wei-Lun Hsu
 * @version 1.0
 * @date 2022/07/12
 * @license
 * @description
 */


#include "zb32l03x_hal.h"

/** @addtogroup ZB32L03x_HAL_Driver
 * @{
 */


/** @defgroup SPI SPI
 * @brief SPI HAL module driver.
 * @{
 */
#if defined(HAL_SPI_MODULE_ENABLED)

//=============================================================================
//                  Constant Definition
//=============================================================================
/**
 *  In master mode, CS signal pull high for slave assigning data between bytes
 *  delay_ticks = (4 * CONFIG_SPI_CS_DELAY_TIMES)
 */
#define CONFIG_SPI_CS_DELAY_TIMES           40
#define CONFIG_SPI_END_TAG                  0xFFA5

#if defined(CONFIG_USE_ZB32L003)
    #define CONFIG_SPI_IRQn             SPI_IRQn
#elif defined(CONFIG_USE_ZB32L030) || defined(CONFIG_USE_ZB32L032)
    #define CONFIG_SPI_IRQn             SPI0_1_IRQn
#endif
//=============================================================================
//                  Macro Definition
//=============================================================================
#define __SPI_FORCE_DISABLE_ISR(irq_id)                 \
            do {                                        \
                HAL_NVIC_DisableIRQ((irq_id));          \
                HAL_NVIC_ClearPendingIRQ((irq_id));     \
            } while(0)

#define __SPI_FORCE_ENABLE_ISR(irq_id)                  \
            do {                                        \
                HAL_NVIC_ClearPendingIRQ((irq_id));     \
                HAL_NVIC_EnableIRQ((irq_id));           \
            } while(0)

#define __IS_SPI_MASTER(__HANDLE__)     ((__HANDLE__)->Instance->CR & SPI_CR_MSTR_Msk)


#define IS_SPI_MODE(MODE)               (((MODE) == HAL_SPI_MODE_MASTER) || ((MODE) == HAL_SPI_MODE_SLAVE))

#define IS_SPI_CPOL(CPOL)               (((CPOL) == HAL_SPI_POLARITY_LOW) || ((CPOL) == HAL_SPI_POLARITY_HIGH))

#define IS_SPI_CPHA(CPHA)               (((CPHA) == HAL_SPI_PHASE_1EDGE) || ((CPHA) == HAL_SPI_PHASE_2EDGE))

#define IS_SPI_NSS(NSS)                 (((NSS) == HAL_SPI_NSS_LOW) || ((NSS) == HAL_SPI_NSS_HIGH))

#define IS_SPI_PRESCALER(PRESCALER)     (((PRESCALER) == HAL_SPI_PCLK_PRESCALER_2)   || \
                                         ((PRESCALER) == HAL_SPI_PCLK_PRESCALER_4)   || \
                                         ((PRESCALER) == HAL_SPI_PCLK_PRESCALER_8)   || \
                                         ((PRESCALER) == HAL_SPI_PCLK_PRESCALER_16)  || \
                                         ((PRESCALER) == HAL_SPI_PCLK_PRESCALER_32)  || \
                                         ((PRESCALER) == HAL_SPI_PCLK_PRESCALER_64)  || \
                                         ((PRESCALER) == HAL_SPI_PCLK_PRESCALER_128))

#if defined(CONFIG_USE_ZB32L003)
    #define IS_SPI_INSTANCE(INSTANCE)       (((INSTANCE) == SPI0))
#elif defined(CONFIG_USE_ZB32L030) || defined(CONFIG_USE_ZB32L032)
    #define IS_SPI_INSTANCE(INSTANCE)       (((INSTANCE) == SPI0) || ((INSTANCE) == SPI1))
#endif
//=============================================================================
//                  Structure Definition
//=============================================================================

//=============================================================================
//                  Global Data Definition
//=============================================================================

//=============================================================================
//                  Private Function Definition
//=============================================================================
/**
 * @brief  Initializes the SPI MSP.
 * @param  hSPI:   pointer to a CRC_HandleTypeDef structure that contains
 *                 the configuration information for CRC
 * @retval None
 */
__weak void HAL_SPI_MspInit(SPI_HandleTypeDef *pHSpi)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(pHSpi);
}

/**
 * @brief  DeInitializes the SPI MSP.
 * @param  hSPI:   pointer to a CRC_HandleTypeDef structure that contains
 *                 the configuration information for CRC
 * @retval None
 */
__weak void HAL_SPI_MspDeInit(SPI_HandleTypeDef *pHSpi)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(pHSpi);
}


__weak void HAL_SPI_TxCmpltCallback(SPI_HandleTypeDef *pHSpi)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(pHSpi);
}

__weak void HAL_SPI_RxCmpltCallback(SPI_HandleTypeDef *pHSpi)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(pHSpi);
}


__weak void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *pHSpi)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(pHSpi);
}
//=============================================================================
//                  Public Function Definition
//=============================================================================
HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *pHSpi)
{
    if( pHSpi == NULL )
        return HAL_ERROR;

    /* Check the parameters */
    assert_param(pHSpi);
    assert_param(IS_SPI_INSTANCE(pHSpi->Instance));
    assert_param(IS_SPI_MODE(pHSpi->Init.Mode));
    assert_param(IS_SPI_CPOL(pHSpi->Init.CLKPolarity));
    assert_param(IS_SPI_CPHA(pHSpi->Init.CLKPhase));
    assert_param(IS_SPI_NSS(pHSpi->Init.NSS));
    assert_param(IS_SPI_PRESCALER(pHSpi->Init.BaudRatePrescaler));

    if( pHSpi->State == HAL_SPI_STATE_RESET )
    {
        /* Allocate lock resource and initialize it */
        __HAL_UNLOCK(pHSpi);

        /* Init the low level hardware : GPIO, CLOCK, NVIC... */
        HAL_SPI_MspInit(pHSpi);
    }

    pHSpi->State = HAL_SPI_STATE_BUSY;

    /* Disable the selected SPI peripheral */
    __HAL_SPI_DISABLE(pHSpi);

    {
        int     prescaler = (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_4)   ? 4   :
                            (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_8)   ? 8   :
                            (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_16)  ? 16  :
                            (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_32)  ? 32  :
                            (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_64)  ? 64  :
                            (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_128) ? 128 :
                            2;

        /**
         *  Delay 1.5T of SCK
         */
        pHSpi->Init.DelayTicks = (prescaler * HAL_RCC_GetHCLKFreq()) / HAL_RCC_GetPCLKFreq();
        pHSpi->Init.DelayTicks = (pHSpi->Init.DelayTicks * 3) >> 1;
    }

    /**
     *  Configure SPI
     *  - SPI Mode
     *  - Clock polarity and phase
     *  - NSS management
     *  - Communication speed
     */
    WRITE_REG(pHSpi->Instance->CR, (pHSpi->Init.Mode | pHSpi->Init.BaudRatePrescaler |
                                    pHSpi->Init.CLKPolarity | pHSpi->Init.CLKPhase));

    /* Configure : NSS management */
    WRITE_REG(pHSpi->Instance->SSN, pHSpi->Init.NSS);

    pHSpi->Init.DelayTicks = (pHSpi->Init.DelayTicks)
                           ? pHSpi->Init.DelayTicks : CONFIG_SPI_CS_DELAY_TIMES;

    /* Enable the selected SPI peripheral */
    __HAL_SPI_ENABLE(pHSpi);

    pHSpi->State = HAL_SPI_STATE_READY;

    return HAL_OK;
}


HAL_StatusTypeDef HAL_SPI_DeInit(SPI_HandleTypeDef *pHSpi)
{
    if( pHSpi == NULL )
        return HAL_ERROR;

    assert_param(pHSpi);
    assert_param(IS_SPI_INSTANCE(pHSpi->Instance));

    __HAL_LOCK(pHSpi);

    pHSpi->State = HAL_SPI_STATE_BUSY;

    /* Disable the SPI Peripheral Clock */
    __HAL_SPI_DISABLE(pHSpi);

    /* DeInit the low level hardware: GPIO, CLOCK, NVIC... */
    HAL_SPI_MspDeInit(pHSpi);

    pHSpi->ErrorCode = HAL_SPI_ERROR_NONE;
    pHSpi->State     = HAL_SPI_STATE_RESET;

    __HAL_UNLOCK(pHSpi);

    return HAL_OK;
}


/**
 *  @brief  SPI transmits an amount of data in blocking mode.
 *
 *  @param [in] pHSpi       The handle pointer of a SPI_HandleTypeDef
 *  @param [in] pData       Pointer to data buffer
 *  @param [in] Size        Amount of data to be sent
 *  @param [in] Timeout_ms  Timeout duration (unit: msec)
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *pHSpi, uint8_t *pData, uint16_t Size, uint32_t Timeout_ms)
{
    uint16_t        NBytes = 0;

    if( !pHSpi || !pData || !Size )
        return HAL_ERROR;

    assert_param(pHSpi);
    assert_param(pData);
    assert_param(Size);

    if( pHSpi->State != HAL_SPI_STATE_READY )
        return HAL_BUSY;

    __HAL_LOCK(pHSpi);

    __SPI_FORCE_DISABLE_ISR(CONFIG_SPI_IRQn);

    pHSpi->State     = HAL_SPI_STATE_BUSY_TX;
    pHSpi->ErrorCode = HAL_SPI_ERROR_NONE;

    __HAL_SPI_DISABLE(pHSpi);

    if( __IS_SPI_MASTER(pHSpi) )
    {
        __HAL_SPI_SET_NSS_HIGH(pHSpi);
    }

    __HAL_SPI_ENABLE(pHSpi);

    NBytes = 0;
    while( NBytes < Size )
    {
        volatile uint32_t   status = 0;
        uint32_t            start_ticks = 0;

        start_ticks = HAL_GetTick();

        if( __IS_SPI_MASTER(pHSpi) )
        {
            __HAL_SPI_SET_NSS_LOW(pHSpi);
        }

        WRITE_REG(pHSpi->Instance->DATA, *pData++);

        do {
            status = READ_REG(pHSpi->Instance->SR);
            if( Timeout_ms != HAL_SPI_BLOCKING &&
                (HAL_GetTick() - start_ticks) > Timeout_ms )
            {
                pHSpi->ErrorCode = HAL_SPI_ERROR_TIMEOUT;
                Size = 0;

                __HAL_SPI_DISABLE(pHSpi);
                break;
            }
        } while( (status & HAL_SPI_FLAG_SPIF) == LOW );

        start_ticks = READ_REG(pHSpi->Instance->DATA);

        if( __IS_SPI_MASTER(pHSpi) )
        {
            __HAL_SPI_SET_NSS_HIGH(pHSpi);
        }

        NBytes++;

        /**
         *  Delay for slave.
         *  In master mode, delay for slave assigning value to data register when CS is high.
         *  In slave mode, delay for syncing master SPIF signal.
         */
        for(int j = 0; j < pHSpi->Init.DelayTicks; j++)
            __asm("nop");
    }

    pHSpi->State = HAL_SPI_STATE_READY;

    __HAL_UNLOCK(pHSpi);

    return (pHSpi->ErrorCode == HAL_SPI_ERROR_NONE) ? HAL_OK : HAL_ERROR;
}


/**
 *  @brief  SPI receives an amount of data in blocking mode.
 *
 *  @param [in] pHSpi       The handle pointer of a SPI_HandleTypeDef
 *  @param [in] pData       Pointer to data buffer
 *  @param [in] pSize       Amount of data to be received
 *  @param [in] Timeout_ms  Timeout duration (unit: msec)
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *pHSpi, uint8_t *pData, uint16_t *pSize, uint32_t Timeout_ms)
{
    uint16_t    Size = *pSize;
    uint16_t    rxBytes = 0;

    if( !pHSpi || !pData || !pSize || *pSize == 0 )
        return HAL_ERROR;

    assert_param(pHSpi);
    assert_param(pData);
    assert_param(pSize);
    assert_param(*pSize);

    if( pHSpi->State != HAL_SPI_STATE_READY )
        return HAL_BUSY;

    __HAL_LOCK(pHSpi);

    __SPI_FORCE_DISABLE_ISR(CONFIG_SPI_IRQn);

    pHSpi->State     = HAL_SPI_STATE_BUSY_RX;
    pHSpi->ErrorCode = HAL_SPI_ERROR_NONE;

    __HAL_SPI_DISABLE(pHSpi);

    if( __IS_SPI_MASTER(pHSpi) )
    {
        __HAL_SPI_SET_NSS_HIGH(pHSpi);
    }

    __HAL_SPI_ENABLE(pHSpi);

    while( rxBytes < Size )
    {
        volatile uint32_t   status = 0;
        uint32_t            start_ticks = 0;

        start_ticks = HAL_GetTick();

        if( __IS_SPI_MASTER(pHSpi) )
        {
            __HAL_SPI_SET_NSS_LOW(pHSpi);
        }

        WRITE_REG(pHSpi->Instance->DATA, 0);

        do {
            status = READ_REG(pHSpi->Instance->SR);
            if( Timeout_ms != HAL_SPI_BLOCKING &&
                (HAL_GetTick() - start_ticks) > Timeout_ms )
            {
                pHSpi->ErrorCode = HAL_SPI_ERROR_TIMEOUT;
                Size = 0;
                rxBytes--;
                __HAL_SPI_DISABLE(pHSpi);
                break;
            }
        } while( (status & HAL_SPI_FLAG_SPIF) == LOW );

        if( __IS_SPI_MASTER(pHSpi) )
        {
            __HAL_SPI_SET_NSS_HIGH(pHSpi);
        }

        *pData++ = pHSpi->Instance->DATA;
        rxBytes++;

        /**
         *  Delay for slave.
         *  In master mode, delay for slave assigning value to data register when CS is high.
         *  In slave mode, delay for syncing master SPIF signal.
         */
        for(int j = 0; j < pHSpi->Init.DelayTicks; j++)
            __asm("nop");
    }

    *pSize = rxBytes;

    pHSpi->State = HAL_SPI_STATE_READY;

    __HAL_UNLOCK(pHSpi);

    return (pHSpi->ErrorCode == HAL_SPI_ERROR_NONE) ? HAL_OK : HAL_ERROR;
}

/**
 *  @brief  Transmit and Receive an amount of data in blocking mode.
 *              It is a 'Full-Duplex' Communication process.
 *
 *  @param [in] pHSpi       The handle pointer of a SPI_HandleTypeDef
 *  @param [in] pTxData     Pointer to transmission data buffer
 *  @param [in] pRxData     Pointer to reception data buffer
 *  @param [in] Size        Amount of data to be sent and received
 *  @param [in] Timeout_ms  Timeout duration (unit: msec)
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *pHSpi,
                                          uint8_t *pTxData, uint8_t *pRxData,
                                          uint16_t Size, uint32_t Timeout_ms)
{
    uint16_t        NBytes = 0;

    if( !pHSpi || !pTxData || !pRxData || !Size )
        return HAL_ERROR;

    assert_param(pHSpi);
    assert_param(pTxData);
    assert_param(pRxData);
    assert_param(Size);

    if( pHSpi->State != HAL_SPI_STATE_READY )
        return HAL_BUSY;

    __HAL_LOCK(pHSpi);

    __SPI_FORCE_DISABLE_ISR(CONFIG_SPI_IRQn);

    pHSpi->State     = HAL_SPI_STATE_BUSY_TX_RX;
    pHSpi->ErrorCode = HAL_SPI_ERROR_NONE;

    __HAL_SPI_DISABLE(pHSpi);

    if( __IS_SPI_MASTER(pHSpi) )
    {
        __HAL_SPI_SET_NSS_HIGH(pHSpi);
    }

    __HAL_SPI_ENABLE(pHSpi);

    NBytes = 0;
    while( NBytes < Size )
    {
        volatile uint32_t   status = 0;
        uint32_t            start_ticks = 0;

        start_ticks = HAL_GetTick();

        if( __IS_SPI_MASTER(pHSpi) )
        {
            __HAL_SPI_SET_NSS_LOW(pHSpi);
        }

        WRITE_REG(pHSpi->Instance->DATA, *pTxData++);

        do {
            status = READ_REG(pHSpi->Instance->SR);
            if( Timeout_ms != HAL_SPI_BLOCKING &&
                (HAL_GetTick() - start_ticks) > Timeout_ms )
            {
                pHSpi->ErrorCode = HAL_SPI_ERROR_TIMEOUT;
                Size = 0;

                __HAL_SPI_DISABLE(pHSpi);
                break;
            }
        } while( (status & HAL_SPI_FLAG_SPIF) == LOW );

        *pRxData++ = READ_REG(pHSpi->Instance->DATA);

        if( __IS_SPI_MASTER(pHSpi) )
        {
            __HAL_SPI_SET_NSS_HIGH(pHSpi);
        }

        NBytes++;

        /**
         *  Delay for slave.
         *  In master mode, delay for slave assigning value to data register when CS is high.
         *  In slave mode, delay for syncing master SPIF signal.
         */
        for(int j = 0; j < pHSpi->Init.DelayTicks; j++)
            __asm("nop");

    }

    pHSpi->State = HAL_SPI_STATE_READY;

    __HAL_UNLOCK(pHSpi);

    return (pHSpi->ErrorCode == HAL_SPI_ERROR_NONE) ? HAL_OK : HAL_ERROR;
}



/**
 *  @brief  Transmit an amount of data in non-blocking mode with Interrupt.
 *
 *  @param [in] pHSpi       The handle pointer of a SPI_HandleTypeDef
 *  @param [in] pData       Pointer to data buffer
 *  @param [in] Size        Amount of data to be sent
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_SPI_Transmit_IT(SPI_HandleTypeDef *pHSpi, uint8_t *pData, uint16_t Size)
{
    if( !pHSpi || !pData || !Size )
        return HAL_ERROR;

    assert_param(pHSpi);
    assert_param(pData);
    assert_param(Size);

    if( pHSpi->State != HAL_SPI_STATE_READY )
        return HAL_BUSY;

    __HAL_LOCK(pHSpi);

    __HAL_SPI_DISABLE(pHSpi);

    __SPI_FORCE_ENABLE_ISR(CONFIG_SPI_IRQn);

    pHSpi->State     = HAL_SPI_STATE_BUSY_TX;
    pHSpi->ErrorCode = HAL_SPI_ERROR_NONE;

    if( __IS_SPI_MASTER(pHSpi) )
    {
        __HAL_SPI_SET_NSS_HIGH(pHSpi);
    }

    __HAL_SPI_ENABLE(pHSpi);

    pHSpi->pTxBuffPtr  = pData + 1;
    pHSpi->TxXferSize  = Size;
    pHSpi->TxXferCount = Size - 1;

    pHSpi->pRxBuffPtr  = 0;
    pHSpi->RxXferCount = Size;

    if( __IS_SPI_MASTER(pHSpi) )
    {
        __HAL_SPI_SET_NSS_LOW(pHSpi);
    }

    WRITE_REG(pHSpi->Instance->DATA, *pData);

    __HAL_UNLOCK(pHSpi);

    return HAL_OK;
}


/**
 *  @brief  Receive an amount of data in non-blocking mode with Interrupt.
 *
 *  @param [in] pHSpi       The handle pointer of a SPI_HandleTypeDef
 *  @param [in] pData       Pointer to data buffer
 *  @param [in] pSize       Amount of data to be received
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_SPI_Receive_IT(SPI_HandleTypeDef *pHSpi, uint8_t *pData, uint16_t Size)
{
    if( !pHSpi || !pData || !Size )
        return HAL_ERROR;

    assert_param(pHSpi);
    assert_param(pData);
    assert_param(Size);

    if( pHSpi->State != HAL_SPI_STATE_READY )
        return HAL_BUSY;

    __HAL_LOCK(pHSpi);

    __HAL_SPI_DISABLE(pHSpi);

    __SPI_FORCE_ENABLE_ISR(CONFIG_SPI_IRQn);

    pHSpi->State     = HAL_SPI_STATE_BUSY_RX;
    pHSpi->ErrorCode = HAL_SPI_ERROR_NONE;

    if( __IS_SPI_MASTER(pHSpi) )
    {
        __HAL_SPI_SET_NSS_HIGH(pHSpi);
    }

    __HAL_SPI_ENABLE(pHSpi);

    pHSpi->pRxBuffPtr  = pData;
    pHSpi->RxXferSize  = Size;
    pHSpi->RxXferCount = Size;

    pHSpi->pTxBuffPtr  = 0;
    pHSpi->TxXferCount = Size;

    if( __IS_SPI_MASTER(pHSpi) )
    {
        __HAL_SPI_SET_NSS_LOW(pHSpi);
        WRITE_REG(pHSpi->Instance->DATA, 0x0);
    }

    __HAL_UNLOCK(pHSpi);

    return HAL_OK;
}


/**
 *  @brief  Transmit and Receive an amount of data in non-blocking mode with Interrupt.
 *              It is a 'Full-Duplex' Communication process.
 *
 *  @param [in] pHSpi       The handle pointer of a SPI_HandleTypeDef
 *  @param [in] pTxData     Pointer to transmission data buffer
 *  @param [in] pRxData     Pointer to reception data buffer
 *  @param [in] Size        Amount of data to be sent and received
 *  @return
 *      HAL status
 */
HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT(SPI_HandleTypeDef *pHSpi,
                                             uint8_t *pTxData, uint8_t *pRxData,
                                             uint16_t Size)
{
    if( !pHSpi || !pTxData || !pRxData || !Size )
        return HAL_ERROR;

    assert_param(pHSpi);
    assert_param(pTxData);
    assert_param(pRxData);
    assert_param(Size);

    if( pHSpi->State != HAL_SPI_STATE_READY )
        return HAL_BUSY;

    __HAL_LOCK(pHSpi);

    __HAL_SPI_DISABLE(pHSpi);

    __SPI_FORCE_ENABLE_ISR(CONFIG_SPI_IRQn);

    pHSpi->State     = HAL_SPI_STATE_BUSY_TX_RX;
    pHSpi->ErrorCode = HAL_SPI_ERROR_NONE;

    if( __IS_SPI_MASTER(pHSpi) )
    {
        __HAL_SPI_SET_NSS_HIGH(pHSpi);
    }

    __HAL_SPI_ENABLE(pHSpi);

    pHSpi->pRxBuffPtr  = pRxData;
    pHSpi->RxXferSize  = Size;
    pHSpi->RxXferCount = Size;

    pHSpi->pTxBuffPtr  = pTxData + 1;
    pHSpi->TxXferSize  = Size;
    pHSpi->TxXferCount = Size - 1;

    if( __IS_SPI_MASTER(pHSpi) )
    {
        __HAL_SPI_SET_NSS_LOW(pHSpi);
    }

    WRITE_REG(pHSpi->Instance->DATA, *pTxData);

    __HAL_UNLOCK(pHSpi);

    return HAL_OK;
}

/**
 *  @brief  Get frequency of SCK of MASTER mode.
 *
 *  @param [in] pHSpi       The handle pointer of a SPI_HandleTypeDef
 *  @return
 *      SCK frequency.
 *      ps. It will return 0 if slave mode or something fail.
 */
uint32_t HAL_SPI_Get_SCK(SPI_HandleTypeDef *pHSpi)
{
    uint32_t        sck = 0;

    if( !pHSpi || pHSpi->State == HAL_SPI_STATE_RESET )
        return 0;

    assert_param(pHSpi);

    if( __IS_SPI_MASTER(pHSpi) )
    {
        int     prescaler = (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_4)   ? 4   :
                            (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_8)   ? 8   :
                            (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_16)  ? 16  :
                            (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_32)  ? 32  :
                            (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_64)  ? 64  :
                            (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_128) ? 128 :
                            2;

        sck = HAL_RCC_GetPCLKFreq() / prescaler;
    }

    return sck;
}

/**
 *  @brief  Calculate delay ticks
 *
 *  @param [in] pHSpi       The handle pointer of a SPI_HandleTypeDef
 *  @param [in] spi_sck     The SCK frequency of remote master.
 *  @return
 *      delay ticks
 */
uint16_t HAL_SPI_Calc_Delay(SPI_HandleTypeDef *pHSpi, uint32_t spi_sck)
{
    uint16_t    delay_ticks = 0;

    if( !pHSpi || !spi_sck || pHSpi->State == HAL_SPI_STATE_RESET )
        return 0;

    assert_param(pHSpi);
    assert_param(spi_sck);

    if( __IS_SPI_MASTER(pHSpi) )
    {
        int     prescaler = (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_4)   ? 4   :
                            (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_8)   ? 8   :
                            (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_16)  ? 16  :
                            (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_32)  ? 32  :
                            (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_64)  ? 64  :
                            (pHSpi->Init.BaudRatePrescaler ==  HAL_SPI_PCLK_PRESCALER_128) ? 128 :
                            2;

        delay_ticks = (prescaler * HAL_RCC_GetHCLKFreq()) / HAL_RCC_GetPCLKFreq();
        delay_ticks <<= 1;
    }
    else
    {
        delay_ticks = HAL_RCC_GetHCLKFreq() / spi_sck;
    }

    return delay_ticks;
}

/**
 *  @brief  This function handles SPI interrupt request.
 *
 *  @param [in] pHSpi       The handle pointer of a SPI_HandleTypeDef
 *      None
 */
void HAL_SPI_IRQHandler(SPI_HandleTypeDef *pHSpi)
{
    volatile uint32_t   status = READ_REG(pHSpi->Instance->SR);
    uint8_t             data = 0;

    if( __IS_SPI_MASTER(pHSpi) )
    {
        __HAL_SPI_SET_NSS_HIGH(pHSpi);
    }

    do {
        if( !(status & HAL_SPI_FLAG_SPIF) )
            break;

        data = (uint8_t)READ_REG(pHSpi->Instance->DATA);

        if( pHSpi->pRxBuffPtr )
        {
            *pHSpi->pRxBuffPtr++ = data;
            pHSpi->RxXferCount--;
            if( pHSpi->RxXferCount == 0 )
            {
                __HAL_SPI_DISABLE(pHSpi);

                HAL_SPI_RxCmpltCallback(pHSpi);

                pHSpi->pRxBuffPtr = 0;
                pHSpi->State      = HAL_SPI_STATE_READY;
                break;
            }
        }

        if( pHSpi->TxXferCount == CONFIG_SPI_END_TAG &&
            __IS_SPI_MASTER(pHSpi))
        {
            __HAL_SPI_DISABLE(pHSpi);
            break;
        }

        /**
         *  Delay for slave.
         *  In master mode, delay for slave assigning value to data register when CS is high.
         *  In slave mode, delay for syncing master SPIF signal.
         */
        for(int j = 0; j < pHSpi->Init.DelayTicks; j++)
            __asm("nop");

        if( __IS_SPI_MASTER(pHSpi) )
        {
            __HAL_SPI_SET_NSS_LOW(pHSpi);
        }

        WRITE_REG(pHSpi->Instance->DATA, (pHSpi->pTxBuffPtr) ? *pHSpi->pTxBuffPtr++ : 0x00);

        if( pHSpi->pTxBuffPtr )
        {
            if( pHSpi->TxXferCount )
                pHSpi->TxXferCount--;
            if( pHSpi->TxXferCount == 0 )
            {
                HAL_SPI_TxCmpltCallback(pHSpi);
                pHSpi->pTxBuffPtr  = 0;
                pHSpi->State       = HAL_SPI_STATE_READY;
                pHSpi->TxXferCount = CONFIG_SPI_END_TAG;
            }
        }
    } while(0);

    if( (status & HAL_SPI_FLAG_MDF) )
    {
        HAL_SPI_ErrorCallback(pHSpi);
        WRITE_REG(pHSpi->Instance->CR, pHSpi->Instance->CR);
    }

    if( (status & (HAL_SPI_FLAG_WCOL | HAL_SPI_FLAG_SPIF)) == HAL_SPI_FLAG_WCOL )
    {
        HAL_SPI_ErrorCallback(pHSpi);
        data = (uint8_t)READ_REG(pHSpi->Instance->DATA);
    }

    return;
}


#endif  /* HAL_SPI_MODULE_ENABLED */

/**
 * @}
 */  /* group SPI SPI */

/**
 * @}
 */  /* group ZB32L03x_HAL_Driver */
